package timbap.visualizer.api.model;

import java.util.Date;

/**
 * The <code>MappedTimeCodeInterpolation</code> is a specific
 * implementation of the <code>TimeCode</code> interface
 * that calculates its absolute position from the velocity
 * of the given time code and a given velocity factor.
 *
 * The absolute position is only reset to the value of the
 * given time code, as soon as a timecode jump occurs that exceeds
 * a certain threshold.
 *
 * @author  Andreas Pabst
 * @see     timbap.visualizer.api.model.AbstractTimeCode
 * @see     timbap.visualizer.api.model.TimeCode
 */
public class MappedTimeCodeInterpolation extends AbstractTimeCode {
    
    private static final float MINIMUM_RELATIVE_TIMECODE_JUMP_LENGTH = 0.005f;
    
    private long timeAtLastInterpolationCalculation;
    private float interpolatedAbsolutePosition;
    
    private float mappingVelocityFactor;
    private TimeCode sourceTimeCode;

    public MappedTimeCodeInterpolation(float pPMappingVelocityFactor, TimeCode pSourceTimeCode) {
        super();
        this.sourceTimeCode = pSourceTimeCode;
        this.mappingVelocityFactor = pPMappingVelocityFactor;
        this.interpolatedAbsolutePosition = pSourceTimeCode.getAbsolutePosition();
    }

    @Override
    public float getMaximumAbsolutePosition() {
        return sourceTimeCode.getMaximumAbsolutePosition();
    }
    
    public void updateInterpolatedTimeCode() {
        
        this.setSignalPower(sourceTimeCode.getSignalPower());
        this.setVelocity(sourceTimeCode.getVelocity());
        this.setFractionPlayed(sourceTimeCode.getFractionPlayed());
        
        long now = new Date().getTime();
        
        float sourceAbsPositionDelta = sourceTimeCode.getNormalizedAbsolutePositionDelta();
        float originalAbsolutePosition = sourceTimeCode.getAbsolutePosition();
        if (Math.abs(sourceAbsPositionDelta) > MINIMUM_RELATIVE_TIMECODE_JUMP_LENGTH) {
            this.setAbsolutePosition(originalAbsolutePosition);
            interpolatedAbsolutePosition = originalAbsolutePosition;
        } else {
            interpolatedAbsolutePosition = interpolatedAbsolutePosition
                + ( (now - timeAtLastInterpolationCalculation)
                        * sourceTimeCode.getVelocity()
                        * mappingVelocityFactor);
            if (interpolatedAbsolutePosition < 0) {
                interpolatedAbsolutePosition = sourceTimeCode.getMaximumAbsolutePosition() - 1;
            } else if (interpolatedAbsolutePosition > sourceTimeCode.getMaximumAbsolutePosition()) {
                interpolatedAbsolutePosition = 1;
            }
            this.setAbsolutePosition(interpolatedAbsolutePosition);
            
            timeAtLastInterpolationCalculation = now;
        }
        this.setOnFlipSide(sourceTimeCode.isOnFlipSide());
    }

}
